import random
from abc import ABCMeta, abstractmethod

ROW = COL = 3  # 棋盘大小
SPACE = "-"  # 空格标签
HUMAN = "HUMAN"  # 人类棋子标签
COMPUTER = "COMPUTER"  # 电脑棋子标签


# 棋盘是否有空位
def empty(board):
    return SPACE in board


# 判断player是否胜利
def winner(board, player):
    """
    --------------
    | 0 || 1 || 2 |
    | 3 || 4 || 5 |
    | 6 || 7 || 8 |
    --------------
    获胜的算法：
    012     345     678
    036     147     258
    048     246
    """
    wins = [
        [board[0], board[1], board[2]],
        [board[3], board[4], board[5]],
        [board[6], board[7], board[8]],
        [board[0], board[3], board[6]],
        [board[1], board[4], board[7]],
        [board[2], board[5], board[8]],
        [board[0], board[4], board[8]],
        [board[2], board[4], board[6]],
    ]
    state = [player, player, player]
    return state in wins


# 定义抽象基类
class Player(metaclass=ABCMeta):

    def __init__(self, chess):
        self.chess = chess

    @abstractmethod
    def move(self, board):
        pass


class Computer(Player):

    def __init__(self, chess="O"):
        super().__init__(chess)

    def minimax(self, board, player, next_player, alpha=-2, beta=2):
        if winner(board, self.chess):
            return +1
        if winner(board, "X" if self.chess == "O" else "O"):
            return -1
        elif not empty(board):
            return 0  # 平局

        for move in range(ROW * COL):
            if board[move] == SPACE:  # 尝试下棋
                board[move] = player  # 记录
                val = self.minimax(
                    board, next_player, player, alpha, beta
                )  # 继续思考对手怎么下棋
                board[move] = SPACE  # 重置

                if player == self.chess:  # 极大 max value
                    if val > alpha:
                        alpha = val
                    if alpha >= beta:  # 剪枝
                        return beta
                else:  # 极小 min value
                    if val < beta:
                        beta = val
                    if beta <= alpha:  # 剪枝
                        return alpha

        return alpha if player == self.chess else beta

    def move(self, board):
        best = -2
        my_moves = []
        for move in range(ROW * COL):
            if board[move] == SPACE:  # 尝试下棋
                board[move] = self.chess  # 记录
                score = self.minimax(
                    board, "X" if self.chess == "O" else "O", self.chess
                )  # 思考对手怎么下棋
                board[move] = SPACE  # 重置

                if score > best:  # 找到更优的位置
                    best = score
                    my_moves = [move]
                if score == best:  # 一样优秀的位置
                    my_moves.append(move)

        pos = random.choice(my_moves)  # 随机挑出一个位置
        board[pos] = self.chess


class Human(Player):

    def __init__(self, chess="X"):
        super().__init__(chess)

    def move(self, board):
        looping = True
        while looping:
            try:
                inp = input(f"玩家({self.chess})请输入下棋位置[1-9]：")
                pos = int(inp) - 1  # 输入的下标从1开始
                if 0 <= pos <= 8:
                    if board[pos] == SPACE:
                        looping = False
                    else:
                        print("此处不允许下棋")
                else:
                    print("输入不合法：[1-9]")
            except ValueError:
                print("输入不合法：Error")

        board[pos] = self.chess


class Game:

    def __init__(self):
        self.board = [SPACE] * (ROW * COL)
        self.computer1 = Computer()
        self.computer2 = Computer()
        self.human1 = Human()
        self.human2 = Human()
        self.current_player = None

        self.select_mode()
        self.select_chess()
        self.select_turn()

    def select_mode(self):
        choice = input(
            "请选择游戏模式：[1] 人类玩家对战电脑玩家, [2] 人类玩家对战人类玩家, [3] 电脑玩家对战电脑玩家 >>> "
        )
        if choice == "1":
            self.human = self.human1
            self.computer = self.computer1
            self.players = [self.human, self.computer]
        elif choice == "2":
            self.human1.chess = "X"
            self.human2.chess = "O"
            self.players = [self.human1, self.human2]
        elif choice == "3":
            self.computer1.chess = "X"
            self.computer2.chess = "O"
            self.players = [self.computer1, self.computer2]
        else:
            print("无效选择，默认选择人类玩家对战电脑玩家")
            self.human = self.human1
            self.computer = self.computer1
            self.players = [self.human, self.computer]

    def select_chess(self):
        if hasattr(self, "human"):
            choice = input("请选择棋子类型：[X/O]，默认'X' >>> ")
            if choice == "O":
                self.computer.chess = "X"
                self.human.chess = "O"
            else:
                self.computer.chess = "O"
                self.human.chess = "X"

    def select_turn(self):
        if hasattr(self, "human"):
            choice = input("请选择是否先手：[T/F]，默认'T' >>> ")
            if choice == "F":
                self.current_player = self.computer
            else:
                self.current_player = self.human
        else:
            self.current_player = self.players[0]

    def switch(self):
        self.current_player = self.players[
            (self.players.index(self.current_player) + 1) % 2
        ]

    def render(self):
        print("--------------")
        for i in range(ROW):
            for j in range(COL):
                k = i * ROW + j
                print(f"| {self.board[k]} ", end="")
            print("|")
        print("--------------")

    def start(self):
        self.render()

        while True:
            self.current_player.move(self.board)
            self.render()

            if winner(self.board, self.human1.chess):
                print("人类玩家1胜利！！！")
                exit(0)
            elif winner(self.board, self.human2.chess):
                print("人类玩家2胜利！！！")
                exit(0)
            elif winner(self.board, self.computer1.chess):
                print("电脑玩家1胜利！！！")
                exit(0)
            elif winner(self.board, self.computer2.chess):
                print("电脑玩家2胜利！！！")
                exit(0)
            elif not empty(self.board):
                print("平局！！！")
                exit(0)

            self.switch()


if __name__ == "__main__":
    Game().start()
